<?php
/*********************** Easy-D ****************************
 @ 文件名称：Route.class.php
 @ 文件名称：路由处理类                   
 @ 文件作用：处理接口路由的解析，转发，验证的一系类与路由相关的操作
 @ 维护人员：weixuan                                      
 ***********************************************************/

namespace coreLib\RouteClass;
use coreLib\ErrorClass\Error;
use coreLib\DbClass\Db;

/**
 * 路由操作类
 * @desc 用于将请求的路由进行分解，获取到响应的请求地址与请求参数
 **/
class Route{

	//数据库操作对象
	protected $_db = NULL;

	/*
	 * 私有属性
	 */
	public    $nowRequestInterfaceInfo;  //当前准备处理的接口
	public    $pathInfo;				 //pathinfo请求路径
	protected $httpHost;	    		 //主机地址
	protected $userAgent;				 //访问设备信息
	protected $serverPort;				 //主机端口
	protected $requestScheme;   		 //请求协议
	
	/*
	 * 向外暴露属性
	 */
	public 	  $restfulParams = [];		 //restful接口请求参数
	public    $bodyParams;			     //body请求参数
	public 	  $requestInterface;  		 //接口请求地址
	public 	  $reuqestFilePath;		     //接口请求的完整文件路径
	public 	  $requestServiceVersion;	 //服务版本号
	public 	  $requestServerNo;	         //服务器请求编号
	public    $requestUri;				 //请求完整路径
	public 	  $requestMethod;			 //请求方式
	public    $pathInfoName;             //接口文件夹
	public    $pathInfoLibUrl;           //请求接口对应modelURL
	public    $interfaceInfo; 		     //接口配置信息
	public 	  $isCURD;					 //是否自动构建CURD
	public 	  $isCache;					 //是进行缓存操作
	public 	  $interfaceType;			 //接口的类型   'BUSSINESS'(业务接口),'SYSTEM'（系统接口）

	/*
	 * 构造函数
	 * @params {$db} 数据库操作对象
	 * @return 没有返回值
	 */
	function __construct($db){

		//获取数据库操作对象
		$this->_db = $db;
		
		//通过SERVER全局数组获取相关路由信息
		$this->pathInfo      = @$_SERVER['PATH_INFO'] ? $_SERVER['PATH_INFO']: $_SERVER['QUERY_STRING'];
		$this->httpHost      = $_SERVER['HTTP_HOST'];
		$this->userAgent     = @$_SERVER['HTTP_USER_AGENT'];
		$this->serverPort    = $_SERVER['SERVER_PORT'];
		$this->requestUri    = $_SERVER['REQUEST_URI'];
		$this->requestMethod = $_SERVER['REQUEST_METHOD'];
		$this->requestScheme = $_SERVER['REQUEST_SCHEME'];

		//获取聚合层服务器编号(服务器验权使用)
		$this->requestServerNo = @$_SERVER['HTTP_SERVER_NO'] ? $_SERVER['HTTP_SERVER_NO'] : NULL;

		//服务器验权操作
		$this->serverAuth();

		//记录相关请求日志
		$this->recordInterfaceLogs();

		//判断是否为一个合法请求
		$this->isConformityUrl();

		//查看访问的接口是否有效（存在）
		$this->isInterfaceValid();

		//进行路由解析
		$this->URIparse();	

		//进行服务器地址与接口白名单验证
		$this->serverVerification();

		//查看接口文件是否存在
		$this->isInterfaceExists();

		//判断缓存配置是否正确
		$this->isCache();

	}

	/*
	 * 服务器验权操作
	 */
	protected function serverAuth(){

		//查看是否独立部署
		if(!$GLOBALS['settings']['isIndependentDeployment']){

			//判断是否需要验权
			if($GLOBALS['settings']['isAggregationServerValidation']){

				//判断是否传递服务器验权参数
				if($this->requestServerNo == NULL){
					Error::Result("REQUEST_ERROR",$GLOBALS['errorMsg']['serverNoError']);
				}

				//根据编号查询服务器是否注册
				$serverData = $this->_db->mysqlDB->params(["as_no" => $this->requestServerNo])->select("sys_aggregation_server");
				
				//判断是否有注册该聚合层服务器
				if(count($serverData['data']) <= 0){
				Error::Result("REQUEST_ERROR",$GLOBALS['errorMsg']['serverNoRegisterError']);
				}

				//判断该聚合层服务器是否开启
				if($serverData['data'][0]['as_is_open'] == 0){
				Error::Result("REQUEST_ERROR",$GLOBALS['errorMsg']['serverNoOpenError']);
				}

			}

		}
	}

	/*
	 * 记录相关请求日志
	 */
	protected function recordInterfaceLogs(){

		//记录请求的方式
		$GLOBALS['settings']['interfaceInfo'][] = "请求方式：" . $this->requestMethod;

	}

	/*
	 * 路由解析
	 * @return 没有返回值
	 */
	protected function URIparse(){

		//获取除去接口外的参数字符串
		$paramsStr = str_replace($this->nowRequestInterfaceInfo[0],"",$this->pathInfo);
		$paramsStr = trim($paramsStr,"/");
		
		//保存最终参数结果
		$paramsRs  = array();

		//判断接口是否有缓存配置
		$nowApi = $this->nowRequestInterfaceInfo[0];

		//获取当前请求的接口配置信息
		$apiInfo = $GLOBALS['interfaceData'][$nowApi];
		$this->interfaceInfo = $apiInfo;


		//如果存在参数
		if(!empty($paramsStr)){

			//判断参数的格式是否正确
			if(preg_match("/^[0-9]+[a-zA-Z_]?/",$paramsStr)){
				Error::Result($this->visitorId,"REQUEST_ERROR",$GLOBALS['errorMsg']['paramsFormatError']);
			}

			//将可能数据分解成为参数数组
			$paramsArr = explode("/",$paramsStr);

			
			for($i=0;$i<count($paramsArr);$i+=2){

				$tempKey   = $paramsArr[$i];

				//判断参数key是否为数字，如果为数组，则为无效key
				if(intval($tempKey)){
				   Error::Result("REQUEST_ERROR",$GLOBALS['errorMsg']['paramsFormatError']);
				}

				$tempValue = $paramsArr[$i+1];
				array_push($paramsRs,$tempKey."=".$tempValue);

			}
		}

		//判断是否有参数
		if(count($paramsRs)>0){

			//临时存放参数解析最终数组
			$tempParamsArr = [];

			//将参数处理成为关联数组（参数名 = 参数值）的格式
			for($i=0;$i<count($paramsRs);$i++){

				$param = explode("=",$paramsRs[$i]);
				$tempParamsArr[$param[0]] = $param[1];
			}

			//保存restful请求参数
			$this->restfulParams = $tempParamsArr;
		}

		//保存请求地址
		$this->requestInterface = $this->nowRequestInterfaceInfo[0];

		//保存body请求参数
		$this->bodyParams = $_POST;
	
	}

	/*
	 * 进行服务器地址与接口白名单验证
	 * @return 没有返回值
	 */
	protected function serverVerification(){

		//获取本地访问的接口名称
		$nowApi = $this->nowRequestInterfaceInfo[0];

		//记录请求原子层的服务器地址
		$GLOBALS['settings']['interfaceInfo'][] = "请求源地址：" . $this->requestScheme."://".$this->httpHost;

		//记录请求的接口名称
		$GLOBALS['settings']['interfaceInfo'][] = "请求接口：" . $nowApi;

		if($GLOBALS['settings']['isWhiteAuth']){
			
			//查看该接口是否在接口白名单中
			if(!(in_array($nowApi,$GLOBALS['interfaceName']))){

				//获取请求服务器的地址+端口号
				$serverAddress = $this->requestScheme."://".$this->httpHost;

				//获取服务器白名单数组
				$whiteListArr = $GLOBALS['serverIP'];

				//判断是否在服务器ip白名单中存在
				if(!(in_array($serverAddress,$whiteListArr))){
					Error::Result("REQUEST_ERROR",$GLOBALS['errorMsg']['noServerAuthorizationError']); 	
				}
			}

		}
		
	}


	/*
	 * 判断请求接口是否合法
	 * @return 没有返回值
	 */
	protected function isConformityUrl(){

		//判断请求方式
		if(!(in_array($this->requestMethod,$GLOBALS['settings']['method']))){
			Error::Result("REQUEST_ERROR",$GLOBALS['errorMsg']['methodError']);
		}

		//判断请求接口是否为空
		if(empty($this->pathInfo)){
			Error::Result("REQUEST_ERROR",$GLOBALS['errorMsg']['interfaceEmptyError']);
		}

	}


	/*
	 * 查看该接口是否有效
	 * @return 没有返回值
	 */
	protected function isInterfaceValid(){

		//匹配的接口数
		$matchCount = 0;
		
		$tempPathArr = explode("/",$this->pathInfo);
		$tempPathStr = "/".$tempPathArr[1]."/".$tempPathArr[2];

		//遍历配置文件中的接口
		foreach($GLOBALS['interfaceData'] as $key=>$value){

			//查看访问的pathinfo是否存在于接口配置文件
			if($tempPathStr == $key){

				//存在，则保存准备处理的接口名称与接口权限
				$this->nowRequestInterfaceInfo = [$key,$value];

				//验证请求方式,如果接口没有指定请求方式，则表示即可以GET也可以POST，否则需要进行验证
				if($this->nowRequestInterfaceInfo[1]['method'] != NULL && $this->nowRequestInterfaceInfo[1]['method'] != 'ALL'){
					
					if($this->requestMethod != $this->nowRequestInterfaceInfo[1]['method']){
						Error::Result("REQUEST_ERROR",$GLOBALS['errorMsg']['interfaceMethodError']);
					}


				}
				
				//累计匹配数
				$matchCount++;
			}
		}	
		
		//不存在该接口
		if($matchCount == 0){

			Error::Result("REQUEST_ERROR",$GLOBALS['errorMsg']['interfaceNoneError']);	
		}

	}

	/*
	 * 查看接口文件是否存在
	 * @return 没有返回值
	 */
	protected function isInterfaceExists(){

		//判断接口的类型
		if(@$this->interfaceInfo['type'] != NULL && $this->interfaceInfo['type'] == 'SYSTEM'){

			$this->interfaceType = "SYSTEM";
		   

		   	//拼接需要访问的文件
			$interfacePath = FRAMEWORK_PATH."/routes".$this->requestInterface.".php";

			//查看该文件是否存在
			if(!file_exists($interfacePath)){

				//自动构建CURD
				if( $GLOBALS['settings']['isCURD'] ) {

					$paramsValidation = $GLOBALS['interfaceSystemPrivilege'][$this->requestInterface];

					if($paramsValidation['autoTable'] == NULL || $paramsValidation['autoType'] == NULL){
						Error::Result("REQUEST_ERROR",$GLOBALS['errorMsg']['autoCURDParamsError']);
					}

					$this->isCURD = true;

				}
			}

		}else{

			$this->interfaceType = "BUSINESS";

			$interfacePath = BUSINESS_PATH.$this->requestInterface.".php";

			//查看该文件是否存在
			if(!file_exists($interfacePath)){

				//自动构建CURD
				if( $GLOBALS['settings']['isCURD'] ) {

					$paramsValidation = $GLOBALS['interfaceData'][$this->requestInterface];

					if($paramsValidation['autoTable'] == NULL || $paramsValidation['autoType'] == NULL){
						Error::Result("REQUEST_ERROR",$GLOBALS['errorMsg']['autoCURDParamsError']);
					}

					$this->isCURD = true;

				}else{
					Error::Result("REQUEST_ERROR",$GLOBALS['errorMsg']['interfaceFileNoneError']);
				}
			}
		   	
		}

		//保存最终访问的文件路径
		$this->reuqestFilePath = $interfacePath;

	}

	/*
	 * 查看缓存配置信息
	 * @return 没有返回值
	 */
	protected function isCache(){
		

		//判断是否开启缓存
		if($GLOBALS['settings']['cache']['isCache']) {

			//判断缓存类型是否正确
			if($GLOBALS['settings']['cache']['cacheType'] != 'file') {
				Error::Result("REQUEST_ERROR",$GLOBALS['errorMsg']['cacheTypeError']);
			}

			//判断缓存目录是否存在
			if(!file_exists(BASE_PATH."/".$GLOBALS['settings']['cache']['cacheDir'])) {
				Error::Result("REQUEST_ERROR",$GLOBALS['errorMsg']['cacheTypeError']);
			}

			//缓存该次请求
			$this->isCache = true;

		}

		//判断接口是否有缓存配置
		$nowApi = $this->nowRequestInterfaceInfo[0];

		//获取当前请求的接口配置信息
		$apiInfo = $GLOBALS['interfaceData'][$nowApi];

		//没有设置缓存
		if(@$apiInfo['isCache'] != NULL && $apiInfo['isCache'] == 'YES'){
			
			if($apiInfo['cacheType'] != 'file'){
				Error::Result("REQUEST_ERROR",$GLOBALS['errorMsg']['apiCacheTypeError']);
			}

			//缓存该次请求
			$this->isCache = true;

		}

	}

}